#!/usr/bin/env python
#-*- coding:utf-8 -*-

import char, logging
from shorp.core.node import Node

VERSION = '20100407'
logger = logging.getLogger('shorp.parser')
match = { '"' : '"', "'" : "'", '(' : ')', '[' : ']', '<' : '>', '{' : '}' }

class ParsedFlag:
    FAIL = 0        # parse failed
    CONTINUE = 1    # continue use this parser
    DONE = 2        # parse done, try another parser on this char
    PASS = 3        # parse done, pass this char
    
class Token:    
    def __init__(self):
        self.idx = 0
        self.token = ['','']
    
    def reset(self):
        self.idx = 0; self.token = ['','']
        
    def isEmpty(self):
        return self.idx == 0 and self.token[0] == ''
        
    def __repr__(self):
        return str(self.token)
        
class Context:
    def __init__(self, p):        
        self.root = Node()
        self.current = Node() 
        self.token = Token()
        self.stack = [self.root]    
        self.parser = p
            
class SubParser:
    def __init__(self, context):
        self.context = context
    def check(self, c):
        return False
    def parse(self, c):
        return ParsedFlag.FAIL
        
class CommentParser(SubParser):
    def check(self, c):
        return c == '/'
    def parse(self, c):        
        if char.isLineSpace(c): return ParsedFlag.PASS
        else: return ParsedFlag.CONTINUE

class SpaceParser(SubParser):
    def check(self, c):
        return char.isSpace(c)
    def parse(self, c):
        if not self.context.current.isEmpty():
            self.context.stack[-1].addChild(self.context.current)
            self.context.current = Node()
        return ParsedFlag.DONE

class TextParser(SubParser):    
    begin = ['"', "'", '[']
    def __init__(self, context):
        SubParser.__init__(self, context)
        self.content = ''
        self.end = None
        
    def check(self, c):
        return c in self.begin
    def parse(self, c):
        if self.content == '' and c in self.begin:
            self.end = match[c]
        elif self.content == '' and c == '^':            
            self.end = '$' + self.end
        elif c == self.end[-1] and (len(self.end) == 1 or self.content[-1] == self.end[0]):
            if len(self.end) != 1:
                self.content = self.content[:-1]
            token = self.context.token
            token.token[token.idx] = self.content
            self.content = ''
            return ParsedFlag.PASS
        else:
            self.content = self.content + c
        return ParsedFlag.CONTINUE

class TokenParser(SubParser):
    parsers = {
        '+' : 'parseDefault',
        ':' : 'parseColon',
        '#' : 'parseSharp',
        '.' : 'parseDot'
    } 
    def __init__(self, context):
        SubParser.__init__(self, context)
        self.signal = ''
        self.content = ''
        
    def check(self, c):
        if c in self.parsers.keys():
            self.signal = c
        elif char.isAlp(c):
            self.signal = '+'
        return self.signal != ''
    
    def parse(self, c):
        if self.signal == c: return ParsedFlag.CONTINUE
        if char.isToken(c):
            self.content = self.content + c
            return ParsedFlag.CONTINUE
        elif getattr(self, self.parsers[self.signal])():
            self.content = ''; self.signal = ''
            return ParsedFlag.DONE
        # attribute already exist
        print ("already exist " + self.content)
        return ParsedFlag.FAIL
        
    def parseDefault(self):    
        ret = self.context.current.setAttr('+', self.content)
        return ret
        
    def parseColon(self):
        return self.context.current.setAttr(':', self.content)
        
    def parseSharp(self):
        return self.context.current.setAttr('#', self.content)
    
    def parseDot(self):
        self.context.current.addAttr('.', self.content)
        return True
        
class AttrParser(SubParser):
    def __init__(self, context):
        SubParser.__init__(self, context)
        self.textparser = TextParser(context)
        self.intext = False
    def check(self, c):
        return c == '('
    def parse(self, c):
        if self.intext:
            ret = self.textparser.parse(c)
            if ret == ParsedFlag.PASS:
                self.intext = False  
        elif self.textparser.check(c):
            self.intext = True
            return self.textparser.parse(c)
        elif c == '(':
            return ParsedFlag.CONTINUE
        elif c == ')':
            self.setAttribute()
            return ParsedFlag.PASS
        elif c == ',':
            return self.setAttribute()
        elif c == '=':
            token = self.context.token
            if token.idx != 0:
                return ParsedFlag.FAIL
            else:
                token.idx = 1
        elif char.isToken(c):
            token = self.context.token
            token.token[token.idx] = token.token[token.idx] + c
        elif not char.isSpace(c):
            return ParsedFlag.FAIL
        return ParsedFlag.CONTINUE
    def setAttribute(self):        
        token = self.context.token
        if not token.isEmpty():
            if token.idx == 0:
                self.context.current.setAtAttr(token.token[0])
            elif token.idx == 1:
                self.context.current.setAttr(token.token[0], token.token[1])
            else:
                return ParsedFlag.FAIL
            
            token.reset()
        return ParsedFlag.CONTINUE

class ChildParser(SubParser):
    def check(self, c):
        return c == '{' or c == '}'
    def parse(self, c):
        if c == '{':
            self.context.stack.append(self.context.current)
            self.context.current = Node()
            self.context.token.reset()
            return ParsedFlag.PASS
        elif c == '}':
            self.context.current = self.context.stack.pop()
            return ParsedFlag.PASS
            

class ScriptParser(TextParser):
    def parse(self, c):
        ret = TextParser.parse(self, c)
        if ret == ParsedFlag.PASS:
            token = self.context.token
            if not token.isEmpty() and token.idx == 0:
                self.context.current.setScript(token.token[0])
            else:                
                return ParsedFlag.FAIL
            token.reset()
        return ret

class Parser:
    
    def parse(self, source):
        source += ' '
        slen = len(source)
        context = Context(self)
        parsers = [CommentParser(context), 
            TokenParser(context), 
            AttrParser(context),
            ChildParser(context),
            ScriptParser(context), 
            SpaceParser(context)]
        activep = None
        
        flag = ParsedFlag.DONE
        for c in source:
            if flag == ParsedFlag.CONTINUE:
                flag = activep.parse(c)
            
            if flag == ParsedFlag.DONE:
                activep = None
                for p in parsers:
                    if p.check(c): flag = p.parse(c)
                    if flag == ParsedFlag.CONTINUE:
                        activep = p
                        break
                        
            if flag == ParsedFlag.PASS:                
                flag = ParsedFlag.DONE
                activep = None

        return context.root